﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using App1AppHandler;
using System.Drawing;
using Reborn_WorldServer;

namespace Reborn_Server.App1AppHandler
{
    public class Pet_Controller
    {
        public int autoTauntAmount = 0;

        public enum PetMotion { Follow, Stay }
        private PetMotion _MotionType = PetMotion.Follow;
        public PetMotion MotionType
        {
            get { return _MotionType; }
            set
            {
                //Setting the creature to follow will remove its attack flag
                if (value == PetMotion.Follow)
                    _CreatureIDToAttack = -1;

                //Remove clear the queued move if we change the motion type
                _MoveTo = null;

                _MotionType = value;
            }
        }

        //Pets owner
        public CreatureBase MasterCreature;
        
        //Parent Creature
        public CreatureBase MyCreature;

        //Reference to the charm buff that made this crit a pet
        //If this is null, then it is a summoned pet that needs to be destroyed when this controller is removed
        public Buff CharmBuff = null;

        private Point? _MoveTo = null;
        public Point? MoveTo
        {
            get
            {
                if (_MoveTo.HasValue == false)
                    return null;

                return _MoveTo.Value;
            }
            set
            {
                //Manually moving the pet will also make it stay
                MotionType = PetMotion.Stay;
                _MoveTo = value;
            }
        }

        private long _CreatureIDToAttack = -1;
        public long CreatureIDToAttack
        {
            get { return _CreatureIDToAttack; }
            set
            {
                //Setting the attack creature, takes the pet off follow so it can chase the creature
                if (value != -1)
                    _MotionType = PetMotion.Follow;

                _CreatureIDToAttack = value;
            }
        }

        //Used to determine if pet needs to chase master up stairs
        private bool MasterOnStairs = false;

        public Pet_Controller(CreatureBase myCreature, CreatureBase masterCreature, MapItem creatureMapItem)
        {
            MasterCreature = masterCreature;
            MyCreature = myCreature;

            if (creatureMapItem != null && (creatureMapItem.TerrainType == "StairsUp" || creatureMapItem.TerrainType == "StairsDown"))
                MasterOnStairs = true;
        }

        public int RunPetAI()
        {
            int delay = 3000;

            PortalInfo portalInfo = null;
            LockItem lockItem = MyCreature.GameHandler.GetLockItem(MyCreature.MapID, MyCreature.Instance);

            lock (lockItem.LockObj)
            {
                if (MyCreature.buffManager.IsStunned() == true || MyCreature.HP < 1)
                    return delay;

                Buff buff = MyCreature.buffManager.GetBuff("Fear");
                if (buff != null)
                {
                    Spell_Fear fear = (Spell_Fear)buff;
                    fear.runAI();
                    return delay;
                }

                Map CurrentMap = MyCreature.GameHandler.GetMap(MyCreature.MapID, MyCreature.Instance);
                List<MapItem> MapItemsToUpdate = new List<MapItem>();
                CreatureBase creatureToAttack = new CreatureBase(MyCreature.GameHandler);

                //Get Creatures Map
                List<MapItem> CreatureMapItemsWithin4 = new List<MapItem>();
                List<MapItem> CreatureMapItemsWithin12 = new List<MapItem>();
                List<Character> CharsWithin12 = new List<Character>();
                MapItem CurrentMapItem = MyCreature.currentMapItem;
                foreach (MapItem mapitem in MyCreature.currentMapItem.mapItemsWithin12)
                {
                    if (mapitem.MapX == MyCreature.MapX && mapitem.MapY == MyCreature.MapY)
                        CurrentMapItem = mapitem;

                    if (Math.Abs(mapitem.MapX - MyCreature.MapX) <= 4 && Math.Abs(mapitem.MapY - MyCreature.MapY) <= 4)
                    {
                        CreatureMapItemsWithin4.Add(mapitem);
                        if (CreatureIDToAttack != -1)
                        {
                            foreach (CreatureBase cb in mapitem.CreaturesBases)
                            {
                                if (cb.CreatureID == CreatureIDToAttack && cb.HP > 0)
                                {
                                    creatureToAttack = cb;
                                    break;
                                }
                            }
                        }
                    }
                    if (Math.Abs(mapitem.MapX - MyCreature.MapX) <= 12 && Math.Abs(mapitem.MapY - MyCreature.MapY) <= 12)
                    {
                        CreatureMapItemsWithin12.Add(mapitem);
                        foreach (CreatureBase cb in mapitem.CreaturesBases)
                        {
                            if (cb is Character)
                                CharsWithin12.Add((Character)cb);
                        }
                    }
                }

                List<Point> hiddenList = new List<Point>();

                //The following is for effiency, we checking any circumstances where we know the pet will not move
                //so we can avoid running uncessary calculations such as the hidden or pathing algs
                bool petStays = false;
                //Pet is on stay and there is no MoveTo
                if (MotionType == PetMotion.Stay && MoveTo.HasValue == false)
                    petStays = true;
                //Pet is on follow but it cannot see anything
                else if (MotionType == PetMotion.Follow && (CurrentMapItem.isDark(MyCreature) == true && MyCreature.buffManager.HasNightVision() == false))
                    petStays = true;
                //Pet is following creature but creature is on same hex
                else if (MotionType == PetMotion.Follow && creatureToAttack.CreatureID != -1 && (creatureToAttack.MapX == MyCreature.MapX && creatureToAttack.MapY == MyCreature.MapY))
                    petStays = true;
                //Pet is following master but master is on same hex
                else if (MotionType == PetMotion.Follow && creatureToAttack.CreatureID == -1 && (MasterCreature.MapX == MyCreature.MapX && MasterCreature.MapY == MyCreature.MapY && MasterCreature.MapID == MyCreature.MapID))
                    petStays = true;
                //Pet is trying to follow creature to attack but it is to far away
                else if (MotionType == PetMotion.Follow && creatureToAttack.CreatureID != -1 && (Math.Abs(creatureToAttack.MapX - MyCreature.MapX) > 4 || Math.Abs(creatureToAttack.MapY - MyCreature.MapY) > 4))
                    petStays = true;
                //Pet is trying to follow master but it is to far away
                else if (MotionType == PetMotion.Follow && creatureToAttack.CreatureID == -1 && (Math.Abs(MasterCreature.MapX - MyCreature.MapX) > 4 || Math.Abs(MasterCreature.MapY - MyCreature.MapY) > 4) || MasterCreature.MapID != MyCreature.MapID)
                    petStays = true;
                //Pet is rooted or cannot move
                else if (CurrentMapItem.CreatureMotion() == 0 || MyCreature.buffManager.IsRooted() != "")
                    petStays = true;
                else if (MoveTo.HasValue == true && MoveTo.Value.X == MyCreature.MapX && MoveTo.Value.Y == MyCreature.MapY)
                {
                    MoveTo = null;
                    petStays = true;
                }

                //We have no obvious matches so run the hidden calculation if there is no MoveTo
                else if (MoveTo == null)
                    hiddenList = CreatureAI.FindHidden(MyCreature, CreatureMapItemsWithin4, false);

                Point PointToMoveTo = new Point();

                //Pet still may move, check to see if destinations are hidden, flag to stay
                if (petStays == false && MotionType == PetMotion.Follow && MoveTo == null)
                {
                    if (creatureToAttack.CreatureID != -1)
                        PointToMoveTo = new Point(creatureToAttack.MapX, creatureToAttack.MapY);
                    else if (MasterCreature.MapID == MyCreature.MapID)
                        PointToMoveTo = new Point(MasterCreature.MapX, MasterCreature.MapY);

                    foreach (Point point in hiddenList)
                    {
                        if (point.X == PointToMoveTo.X && point.Y == PointToMoveTo.Y)
                        {
                            petStays = true;
                            break;
                        }
                    }
                }
                else if (MoveTo != null)
                {
                    PointToMoveTo = MoveTo.Value;
                    _MoveTo = null;
                }

                //Pet is not going to move
                if (petStays == true)
                {
                    //Pet cannot see anything
                    if (CurrentMapItem.isDark(MyCreature) == true && MyCreature.buffManager.HasNightVision() == false)
                    {
                        MasterOnStairs = false;
                        return delay;
                    }
                    if (MyCreature.buffManager.IsRooted() != "")
                        MasterOnStairs = false;

                    if (MasterOnStairs == true && MotionType == PetMotion.Follow)
                    {
                        bool foundMaster = false;
                        foreach (CreatureBase creaturebase in CurrentMapItem.CreaturesBases)
                        {
                            if (creaturebase == MasterCreature)
                            {
                                foundMaster = true;
                                break;
                            }
                        }
                        if (foundMaster == false)
                        {
                            if (Managers.localWorldsManager.ownsWorld(CurrentMapItem.PortalMapID, MyCreature.Instance) == true)
                            {
                                portalInfo = new PortalInfo();
                                portalInfo.PortalToMapID = CurrentMapItem.PortalMapID;
                                portalInfo.PortalToMapX = CurrentMapItem.PortalX;
                                portalInfo.PortalToMapY = CurrentMapItem.PortalY;

                                //Remove Character, Update players
                                CurrentMapItem.CreaturesBases.Remove(this.MyCreature);
                                foreach (Character character in CharsWithin12)
                                {
                                    if (Math.Abs(character.MapX - CurrentMapItem.MapX) <= 4 && Math.Abs(character.MapY - CurrentMapItem.MapY) <= 4)
                                        character.AddMapItem(CurrentMapItem);
                                }
                            }
                            MasterOnStairs = false;
                        }
                    }
                    else
                        MasterOnStairs = false;

                    if (portalInfo == null)
                    {
                        List<CreatureBase> AttackableCreatures = new List<CreatureBase>();
                        foreach (CreatureBase creaturebase in CurrentMapItem.CreaturesBases)
                        {
                            if (creaturebase == creatureToAttack)
                            {
                                AttackableCreatures.Clear();
                                AttackableCreatures.Add(creatureToAttack);
                                break;
                            }
                            if (creaturebase.Faction != MyCreature.Faction || MyCreature.hateManager.GetCreatureHateInfo(creaturebase) != null && creaturebase.HP > 0)
                                AttackableCreatures.Add(creaturebase);
                        }
                        //Nothing to attack, dont do anything
                        if (AttackableCreatures.Count == 0)
                        {
                            return delay;
                        }

                        creatureToAttack = AttackableCreatures[0];
                        if (MyCreature is Creature)
                        {
                            Creature myCrit = (Creature)MyCreature;
                            if (autoTauntAmount != 0)
                                creatureToAttack.hateManager.UpdateList(MyCreature, autoTauntAmount);

                            if (myCrit.AttackCreatureBase(CharsWithin12, creatureToAttack, CreatureMapItemsWithin4, false, out delay) == true)
                                creatureToAttack.Update_Character();
                        }
                        else if (MyCreature is Character)
                        {
                            Character myCharacter = (Character)MyCreature;
                            
                            if (myCharacter.AttackCreatureBase(creatureToAttack, false, out delay) > 0)
                                creatureToAttack.Update_Character();
                        }
                        if (creatureToAttack.HP < 1 && !(creatureToAttack is Character))
                            CurrentMapItem.CreaturesBases.Remove(creatureToAttack);

                        if (creatureToAttack.HP < 1 && creatureToAttack.CreatureID == CreatureIDToAttack)
                            CreatureIDToAttack = -1;

                        foreach (Character character in CharsWithin12)
                        {
                            if (creatureToAttack.HP < 1)
                                character.AddMapItem(CurrentMapItem);
                            else
                                character.UpdateCreatureBaseHp(creatureToAttack);
                        }

                        return delay;
                    }

                }//End Pet Stay

                //////  Start Creature Move Logic  //////////////
                if (portalInfo == null)
                {
                    List<List<MapItem>> Paths = CreatureAI.FindAllPaths(MyCreature, CreatureMapItemsWithin4, CurrentMapItem);

                    List<MapItem> FullChosenPath = new List<MapItem>();
                    int DistanceToTravel = 0;

                    int XDistance = 4;
                    int YDistance = 4;
                    int TotalDistance = 9;

                    int maxMoves = 4;

                    if (MyCreature is Creature)
                    {
                        Creature c = (Creature)MyCreature;
                        maxMoves = c.MaxMoves;
                    }

                    for (int x = 1; x <= maxMoves; x++)
                    {
                        foreach (List<MapItem> path in Paths)
                        {
                            if (x >= path.Count)
                                continue;

                            XDistance = Math.Abs(path[x].MapX - PointToMoveTo.X);
                            YDistance = Math.Abs(path[x].MapY - PointToMoveTo.Y);

                            if (XDistance + YDistance < TotalDistance)
                            {
                                TotalDistance = XDistance + YDistance;
                                FullChosenPath = path;
                                DistanceToTravel = x;
                            }
                            if (TotalDistance == 0)
                                break;
                        }
                        if (TotalDistance == 0)
                            break;
                    }
                    if (FullChosenPath.Count > 0)
                    {
                        List<MapItem> ChosenPath = new List<MapItem>();
                        for (int x = 0; x <= DistanceToTravel; x++)
                        {
                            ChosenPath.Add(FullChosenPath[x]);
                        }
                        //MAKE UPDATES
                        for (int v = 0; v < ChosenPath.Count; v++)
                        {
                            foreach (MapItem mapitem in CreatureMapItemsWithin4)
                            {
                                if (mapitem.MapX == ChosenPath[v].MapX && mapitem.MapY == ChosenPath[v].MapY)
                                {
                                    //FIRST POSITION (REMOVE CREATURE)
                                    if (v == 0)
                                    {
                                        mapitem.CreaturesBases.Remove(MyCreature);
                                        MapItemsToUpdate.Add(mapitem);
                                        break;
                                    }
                                    //MIDDLE MOVE
                                    if (v != ChosenPath.Count - 1)
                                    {
                                        if (mapitem.TerrainType == "Door")
                                        {
                                            if (mapitem.Transparent == false)
                                            {
                                                mapitem.Transparent = true;
                                                mapitem.TerrainUrl = mapitem.TerrainUrl.Replace("Closed", "Open");
                                                MapItemsToUpdate.Add(mapitem);

                                                foreach (Character character in CharsWithin12)
                                                {
                                                    character.PlaySound("DoorOpen", mapitem.MapX, mapitem.MapY);
                                                }
                                                //PlaySound("DoorOpen", 4, mapitem.MapX, mapitem.MapY);
                                            }
                                        }
                                    }
                                    //LAST MOVE
                                    else
                                    {
                                        mapitem.CreaturesBases.Add(MyCreature);
                                        MyCreature.SetPosition(mapitem);
                                        MyCreature.Update_Character();

                                        if (mapitem.TerrainType == "Door")
                                        {
                                            if (mapitem.Transparent == false)
                                            {
                                                mapitem.Transparent = true;
                                                mapitem.TerrainUrl = mapitem.TerrainUrl.Replace("Closed", "Open");
                                                //PlaySound("DoorOpen", 4, mapitem.MapX, mapitem.MapY);

                                                foreach (Character character in CharsWithin12)
                                                {
                                                    character.PlaySound("DoorOpen", mapitem.MapX, mapitem.MapY);
                                                }
                                            }
                                        }
                                        MapItemsToUpdate.Add(mapitem);

                                        MasterOnStairs = false;
                                        if (mapitem.TerrainType == "StairsUp" || mapitem.TerrainType == "StairsDown" && MotionType == PetMotion.Follow)
                                        {
                                            foreach (CreatureBase creaturebase in mapitem.CreaturesBases)
                                            {
                                                if (creaturebase == MasterCreature)
                                                {
                                                    MasterOnStairs = true;
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                    break;
                                }
                            }
                        }
                        List<MapItem> MyMapItems = new List<MapItem>();
                        //Send out mapitem updates to nearbyCharacters
                        foreach (MapItem mapitem in CreatureMapItemsWithin12)
                        {
                            if (Math.Abs(mapitem.MapX - MyCreature.MapX) <= 8 && Math.Abs(mapitem.MapY - MyCreature.MapY) <= 8)
                                MyMapItems.Add(mapitem);

                            foreach (MapItem MItem in MapItemsToUpdate)
                            {
                                if ((mapitem.MapX - MItem.MapX >= -4 && mapitem.MapX - MItem.MapX <= 4) && (mapitem.MapY - MItem.MapY >= -4 && mapitem.MapY - MItem.MapY <= 4))
                                {
                                    foreach (CreatureBase creatureBase in mapitem.CreaturesBases)
                                    {
                                        if (creatureBase is Character && creatureBase != MyCreature)
                                        {
                                            Character character = (Character)creatureBase;
                                            character.AddMapItems(MapItemsToUpdate, false);
                                        }
                                    }
                                    break;
                                }
                            }
                        }
                        if (MyCreature is Character)
                        {
                            Character character = (Character)MyCreature;
                            character.AddMapItems(MyMapItems, true);
                        }
                    }
                }

                //Set this before the lock ends. Any other methods that are waiting for this lock to open for this character
                //Will be stopped and re-invoked when this is set to back to false.
                if (portalInfo != null)
                    MyCreature.setIsChangingMaps(true);

            }//End Lock

            if (portalInfo != null)
            {
                MyCreature.MapID = portalInfo.PortalToMapID;

                LockItem newLock = MyCreature.GameHandler.GetLockItem(portalInfo.PortalToMapID, MyCreature.Instance);
                lock (newLock.LockObj)
                {
                    Map newMap = MyCreature.GameHandler.GetMap(portalInfo.PortalToMapID, MyCreature.Instance);

                    MapItem newMapItem = null;
                    List<Character> CharactersWithin4 = new List<Character>();
                    List<MapItem> MyMapItems = new List<MapItem>();
                    foreach (MapItem mapitem in newMap.MapItems)
                    {
                        if (Math.Abs(mapitem.MapX - portalInfo.PortalToMapX) <= 8 && Math.Abs(mapitem.MapY - portalInfo.PortalToMapY) <= 8)
                            MyMapItems.Add(mapitem);

                        if (Math.Abs(mapitem.MapX - portalInfo.PortalToMapX) <= 4 && Math.Abs(mapitem.MapY - portalInfo.PortalToMapY) <= 4)
                        {
                            foreach (CreatureBase creaturebase in mapitem.CreaturesBases)
                            {
                                if (creaturebase is Character)
                                    CharactersWithin4.Add((Character)creaturebase);
                            }
                        }
                        if (mapitem.MapX == portalInfo.PortalToMapX && mapitem.MapY == portalInfo.PortalToMapY)
                        {
                            MyCreature.SetPosition(mapitem);
                            newMapItem = mapitem;
                            MyCreature.Update_Character();
                        }
                    }
                    newMapItem.CreaturesBases.Add(this.MyCreature);

                    foreach (Character character in CharactersWithin4)
                    {
                        if (character != MyCreature)
                            character.AddMapItem(newMapItem);
                    }
                    if (MyCreature is Character)
                    {
                        Character character = (Character)MyCreature;
                        character.AddMapItems(MyMapItems, true);
                    }

                    MasterOnStairs = false;
                    if ((newMapItem.isDark(MyCreature) == false || MyCreature.buffManager.HasNightVision() == true) && (newMapItem.TerrainType == "StairsUp" || newMapItem.TerrainType == "StairsDown"))
                    {
                        foreach (CreatureBase creaturebase in newMapItem.CreaturesBases)
                        {
                            if (creaturebase == MasterCreature)
                            {
                                MasterOnStairs = true;
                                break;
                            }
                        }
                    }

                    //Release the map transfer. This will invoke any other methods that got stopped due to the map transfer.
                    MyCreature.setIsChangingMaps(false);

                }//End Lock

            }//End Portal

            return delay;
        }


        public void Dismiss()
        {
            //lock (MyCreature.creatureLock)
            //{
                //If this pet is result of a spell, just remove the spell and it will handle
                //the clean up
                if (CharmBuff != null)
                    CharmBuff.RemoveSpell(true, true);

                //This must be a summoned pet, do clean up
                else
                {
                    LockItem lockitem = MyCreature.GameHandler.GetLockItem(MyCreature.MapID, MyCreature.Instance);

                    lock (lockitem.LockObj)
                    {
                        if (MasterCreature is Character)
                        {
                            Character character = (Character)MasterCreature;
                            character.petManager.RemovePet(this);
                        }
                        else if (MasterCreature is Creature)
                        {
                            Creature c = (Creature)MasterCreature;
                            c.removePet(this);
                        }

                        MyCreature.Timer_Regen.Stop();
                        MyCreature.Timer_Regen.Dispose();
                        MyCreature.petController = null;

                        if (MyCreature is Creature)
                        {
                            Creature creature = (Creature)MyCreature;
                            creature.creatureAI.Timer_CreatureRound.Stop();
                            creature.creatureAI.Timer_CreatureRound.Dispose();

                            creature.currentMapItem.parentMap.removeCreatureToProcess(creature);
                        }
                        //Only try to remove the creature from the map if the HP are > 0
                        //This means the user clicked the dismiss button,  If HP are == 0
                        //The creature has been killed and it will already have been removed

                        if (MyCreature.HP > 0)
                        {
                            //Set HP to zero to remove all buffs
                            MyCreature.HP = 0;

                            List<Character> CharsToUpdate = new List<Character>();

                            foreach (MapItem mapitem in MyCreature.currentMapItem.mapItemsWithin4)
                            {
                                if (mapitem.MapX == MyCreature.MapX && mapitem.MapY == MyCreature.MapY)
                                {
                                    mapitem.CreaturesBases.Remove(this.MyCreature);
                                }
                                if (Math.Abs(mapitem.MapX - MyCreature.MapX) <= 4 && Math.Abs(mapitem.MapY - MyCreature.MapY) <= 4)
                                {
                                    foreach (CreatureBase creaturebase in mapitem.CreaturesBases)
                                    {
                                        if (creaturebase is Character)
                                        {
                                            CharsToUpdate.Add((Character)creaturebase);
                                        }
                                    }
                                }
                            }
                            foreach (Character character in CharsToUpdate)
                            {
                                character.AddMapItem(MyCreature.currentMapItem);
                            }
                        }
                    }//End Lock
                }
            //}//End creatureLock
        }
    }
}
